﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.ComponentModel;

namespace WhiteFlow.DevTrackingTool.UI.Controls
{
    /// <summary>
    /// Represents a UI control that helps the user to quickly pick a time.
    /// </summary>
    [TemplatePart(Name = "PART_TbSelectedTime", Type = typeof(TextBox))]
    [TemplatePart(Name = "PART_BtnOpenHoursPopup", Type = typeof(ToggleButton))]
    [TemplatePart(Name = "PART_PopupHours", Type = typeof(Popup))]
    [TemplatePart(Name = "PART_HoursGrid", Type = typeof(Grid))]
    [TemplatePart(Name = "PART_MinutesGrid", Type = typeof(Grid))]
    public class TimePicker : Control
    {
        #region Constants

        public const string PickerButtonsStylePropertyName = "PickerButtonsStyle";

        public const string SelectedTimePropertyName = "SelectedTime";

        public const string IsMinutesSelectionActivePropertyName = "IsMinutesSelectionActive";

        #endregion

        #region Fields

        private TextBox PART_TbSelectedTime;

        private ToggleButton PART_BtnOpenHoursPopup;

        private Popup PART_PopupHours;

        private Grid PART_HoursGrid;
        
        private Grid PART_MinutesGrid;
        
        #endregion

        #region DependencyProperties Registration

        /// <summary>
        /// The style of buttons inside the time picker.
        /// </summary>
        public static readonly DependencyProperty PickerButtonsStyleProperty;

        /// <summary>
        /// The selected time.
        /// </summary>
        public static readonly DependencyProperty SelectedTimeProperty;

        /// <summary>
        /// Specifies whether the active selection mode is set on minutes.
        /// </summary>
        public static readonly DependencyProperty IsMinutesSelectionActiveProperty;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the style of buttons inside the time picker.
        /// </summary>
        /// <value>The style of buttons inside the time picker.</value>
        public Style PickerButtonsStyle
        {
            get
            {
                return (Style)GetValue(PickerButtonsStyleProperty);
            }
            set
            {
                SetValue(PickerButtonsStyleProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the selected time.
        /// </summary>
        /// <value>The selected time.</value>
        public TimeSpan SelectedTime
        {
            get
            {
                return (TimeSpan)GetValue(SelectedTimeProperty);
            }
            set
            {
                SetValue(SelectedTimeProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the active selection mode is set on minutes.
        /// </summary>
        /// <value>
        /// <c>true</c> if the active selection is set on minutes, <c>false</c> otherwise.
        /// </value>
        public bool IsMinutesSelectionActive
        {
            get
            {
                return (bool)GetValue(IsMinutesSelectionActiveProperty);
            }
            set
            {
                SetValue(IsMinutesSelectionActiveProperty, value);
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="TimePicker"/> class.
        /// </summary>
        static TimePicker()
        {
            #region DependencyProperties Registration

            PickerButtonsStyleProperty = DependencyProperty.Register(
                PickerButtonsStylePropertyName,
                typeof(Style),
                typeof(TimePicker),
                new FrameworkPropertyMetadata(null));

            SelectedTimeProperty = DependencyProperty.Register(
                SelectedTimePropertyName,
                typeof(TimeSpan),
                typeof(TimePicker),
                new FrameworkPropertyMetadata(new TimeSpan(0, 0, 0)));

            IsMinutesSelectionActiveProperty = DependencyProperty.Register(
                IsMinutesSelectionActivePropertyName,
                typeof(bool),
                typeof(TimePicker),
                new FrameworkPropertyMetadata(false));

            #endregion

            #region Apply Default Style

            DefaultStyleKeyProperty.OverrideMetadata(
                typeof(TimePicker),
                new FrameworkPropertyMetadata(typeof(TimePicker)));

            #endregion
        }

        #endregion

        #region Override Methods

        /// <summary>
        /// When overridden in a derived class, is invoked whenever application code or internal
        /// processes call <see cref="M:System.Windows.FrameworkElement.ApplyTemplate"/>.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            // FIX: avoids that the designer gets broken.
            if (!DesignerProperties.GetIsInDesignMode(this))
            {
                PART_TbSelectedTime = GetTemplateChild("PART_TbSelectedTime") as TextBox;
                PART_BtnOpenHoursPopup = GetTemplateChild("PART_BtnOpenHoursPopup") as ToggleButton;
                PART_PopupHours = GetTemplateChild("PART_PopupHours") as Popup;
                PART_HoursGrid = GetTemplateChild("PART_HoursGrid") as Grid;
                PART_MinutesGrid = GetTemplateChild("PART_MinutesGrid") as Grid;

                PART_PopupHours.Closed += OnHoursPopupClosed;

                LoadHoursButtons();
                LoadMinutesButtons();
            }
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Called when the hours popup is closed, takes care of de-checking the toggle button that
        /// opens the popup.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnHoursPopupClosed(object sender, EventArgs e)
        {
            PART_BtnOpenHoursPopup.IsChecked = false;
            
            // Clear to initial selection mode.
            IsMinutesSelectionActive = false;
        }

        private void OnHourButtonPreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            SelectedTime = new TimeSpan(Convert.ToInt32((sender as Button).Content), SelectedTime.Minutes, 0);
            IsMinutesSelectionActive = true;

            e.Handled = true;
        }

        private void OnMinuteButtonPreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            SelectedTime = new TimeSpan(SelectedTime.Hours, Convert.ToInt32((sender as Button).Content), 0);
            IsMinutesSelectionActive = false;
            PART_BtnOpenHoursPopup.IsChecked = false;

            e.Handled = true;
        }
        
        #endregion

        #region Public Methods

        #endregion

        #region Private Methods

        private void LoadHoursButtons()
        {
            for (int row = 0; row < 5; row++)
            {
                int offset = (row * 5);

                for (int column = 0; column < 5; column++)
                {
                    int hourNumber = (offset + column);

                    if (hourNumber > 23)
                        break;

                    string hourString = hourNumber.ToString().PadLeft(2, '0');

                    Button hourButton = new Button();
                    hourButton.Content = hourString;
                    hourButton.Style = PickerButtonsStyle;
                    hourButton.PreviewMouseDown += OnHourButtonPreviewMouseDown;

                    PART_HoursGrid.Children.Add(hourButton);
                    Grid.SetRow(hourButton, row);
                    Grid.SetColumn(hourButton, column);
                }
            }
        }

        private void LoadMinutesButtons()
        {
            for (int row = 0; row < 3; row++)
            {
                int offset = (row * 20);

                for (int column = 0; column < 4; column++)
                {
                    int minuteNumber = (offset + ((column) * 5));
                    string minuteString = minuteNumber.ToString().PadLeft(2, '0');

                    Button minuteButton = new Button();
                    minuteButton.Content = minuteString;
                    minuteButton.Style = PickerButtonsStyle;
                    minuteButton.PreviewMouseDown += OnMinuteButtonPreviewMouseDown;

                    PART_MinutesGrid.Children.Add(minuteButton);
                    Grid.SetRow(minuteButton, row);
                    Grid.SetColumn(minuteButton, column);
                }
            }
        }

        #endregion
    }
}